// 库
import lodash from 'lodash';
import { FitAddon } from 'xterm-addon-fit';
import { NotaData as ISqliteNotaData, default as Sqlite } from '~sqlite';
import { defineComponent, SetupContext, getCurrentInstance, ComponentInternalInstance, Ref, ref, reactive, nextTick, computed, watch } from 'vue';

// 组件
import xterm from 'xterm';
import LvMsgBox from '~component/vue3-element/msgbox';
import { IInputComponent } from '~component/vue3-element/type';
import { ElForm, ElMessage, ElNotification } from 'element-plus';
import { useSaveDialog, useRunDialog, useExportDataDialog } from './useDialog';

// 工具
import help from '~common/help';
import electronUtils from '~common/electron';
import { VueMountedCycle } from '~common/vue';
import mousetrap, { bindEscEvent } from '~common/mousetrap';
import { IpcMainEventEnum, IpcRendererEventEnum } from '~common/enum';

// @ts-ignore
import { Github as GithubTheme } from 'xterm-theme';

import packageInfo from '../../../../package.json';
import { dataList as defaultDataList } from '~original/nota/default';

type INotaCate = {
    name: string;
};
type INotaData = ISqliteNotaData & {
    from: 'custom' | 'default';
};

const sqlite: typeof Sqlite = require('~sqlite').default;

export default defineComponent({
    setup(_: unknown, context: SetupContext) {
        const vueMountedCycle = new VueMountedCycle();
        const current = getCurrentInstance() as ComponentInternalInstance;

        const that = new Init(context, current, vueMountedCycle);

        vueMountedCycle.trigger();

        return { that };
    },
});

class Init {
    context: SetupContext;
    current: ComponentInternalInstance;

    // 锁定快捷键
    lockMousetrapEvent: Ref<boolean> = ref(false);
    // 是否锁定快捷键
    isLockMousetrapEvent = computed(() => {
        return this.saveDialog.visible.value || this.runDialog.visible.value || this.exportDataDialog.visible.value || this.lockMousetrapEvent.value;
    });

    // 搜索关键字
    searchKeyWord: Ref<string> = ref('');
    // 搜索输入框 Ref
    searchInputRef: Ref<IInputComponent> = ref({});
    // 过滤数据防抖函数
    filterNotaDataDebounse = lodash.debounce(this.filterNotaData, 500);

    // 当前选择的数据项
    curSelector: {
        index: number | null;
        data: INotaData | null;
    } = reactive({
        index: null,
        data: null,
    });

    // 所有的列表数据
    allNotaData: Ref<Array<INotaData>> = ref([]);
    // 渲染的列表数据
    renderNotaData: Ref<Array<INotaData>> = ref([]);

    // 分类列表
    cateList: Ref<Array<INotaCate>> = ref([]);

    // 终端实例
    terminalInstance: null | xterm.Terminal = null;

    // 保存对话框
    saveDialog = useSaveDialog();
    // 执行命令对话框
    runDialog = useRunDialog();
    // 导出数据对话框
    exportDataDialog = useExportDataDialog();

    constructor(context: SetupContext, current: ComponentInternalInstance, vueMountedCycle: VueMountedCycle) {
        this.context = context;
        this.current = current;

        // onMounted
        vueMountedCycle.push(() => {
            // Show Windows
            electronUtils.ipcRenderer.on(IpcRendererEventEnum.SHOW_WINDOWS, () => {
                this.searchInputFocus();
            });

            // 搜索
            watch(this.searchKeyWord, value => {
                if (value) {
                    this.filterNotaDataDebounse();
                } else {
                    this.filterNotaData();
                }
            });
        });

        this.init();

        this.bindMousetrap();
        this.setDialogCallback();
    }

    /**
     * 初始化
     */
    async init() {
        try {
            let needUpdateStoreVersion = false;

            const configResolveInfo = await sqlite.rendererConfig.search('nota');
            const newStoreVersion = packageInfo.customerBuild.renderer.nota.storeVersion;

            if (configResolveInfo.data) {
                const { storeVersion } = JSON.parse(configResolveInfo.data.config);
                if (newStoreVersion === storeVersion) {
                    // ...
                } else {
                    needUpdateStoreVersion = true;
                }
            } else {
                needUpdateStoreVersion = true;
            }

            // 更新版本号
            if (needUpdateStoreVersion) {
                await sqlite.rendererConfig.update('nota', JSON.stringify({ storeVersion: newStoreVersion }));
            }

            await this.buildNotaData();

            // ...
        } catch (error) {
            console.error(error);
            return Promise.reject();
        }
    }

    /**
     * 构造 Nota 数据列表
     */
    async buildNotaData(initSelect: boolean = true) {
        try {
            const searchDataListResolveInfo = await sqlite.nota.searchDataList();
            if (searchDataListResolveInfo.data) {
                // 用户自定义数据源
                this.allNotaData.value = searchDataListResolveInfo.data.map(t => ({ from: 'custom', ...t }));
                // 默认数据源
                const defaultNotaData = defaultDataList.map(t => {
                    const content = t.content
                        .split('\n')
                        .map(s => s.trim())
                        .join('\n')
                        .replace(/^\n/, '')
                        .replace(/\n$/, '');
                    return { from: 'default' as const, ...t, content };
                });
                this.allNotaData.value.push(...defaultNotaData);
                this.filterNotaData(initSelect);
            }

            // 生成分类数据
            const cateMap: { [name: string]: unknown } = {};
            const cateList = this.allNotaData.value.reduce((acc, cur) => {
                if (!cateMap[cur.cate]) {
                    cateMap[cur.cate] = {};
                    acc.push({ name: cur.cate });
                }
                return acc;
            }, [] as INotaCate[]);
            this.cateList.value = cateList;

            // ...
        } catch (error) {
            console.error(error);
            return Promise.reject();
        }
    }

    /**
     * Bind Mousetrap Event
     */
    bindMousetrap() {
        bindEscEvent();
        mousetrap.bind('enter', () => {
            if (this.isLockMousetrapEvent.value) return;
            if (this.curSelector.data) electronUtils.clipboardWriteText(this.curSelector.data.content);
            electronUtils.ipcRenderer.send(IpcMainEventEnum.BACKEND_WINDOWS);
        });
        mousetrap.bind('ctrl+enter', () => {
            if (this.isLockMousetrapEvent.value) return;
            this.triggerCommandOrScript();
        });
        mousetrap.bind('up', () => {
            if (this.isLockMousetrapEvent.value) return;
            const index = this.curSelector.index;
            if (index === null) {
                this.selectItemData(0);
            } else if (index === 0) {
                this.selectItemData(this.renderNotaData.value.length - 1);
            } else {
                this.selectItemData(index - 1);
            }
        });
        mousetrap.bind('down', () => {
            if (this.isLockMousetrapEvent.value) return;
            let index = this.curSelector.index;
            if (index === null) {
                index = 0;
            } else if (index < this.renderNotaData.value.length - 1) {
                index++;
            } else {
                index = 0;
            }
            this.selectItemData(index);
        });
    }

    /**
     * 设置对话框的回调函数
     */
    setDialogCallback() {
        // 保存对话框确定回调
        this.saveDialog.setConfirmCB(() => {
            (this.current.refs[this.saveDialog.formRefKey] as typeof ElForm).validate().then(async () => {
                try {
                    switch (this.saveDialog.saveType.value) {
                        case 'add': {
                            const { cate, cwd, async, command, script, immediate, content, description } = this.saveDialog.formData;
                            const item: ISqliteNotaData = { id: help.random(), cate, cwd, async, command, script, immediate, content, description };

                            const isNewCate = !this.cateList.value.find(f => f.name === cate);
                            if (isNewCate) this.cateList.value.push({ name: cate });

                            await sqlite.nota.updateDatas([item]);
                            this.buildNotaData();
                            break;
                        }

                        case 'edit': {
                            const target = this.allNotaData.value.find(f => f.id === this.curSelector.data?.id);
                            if (target) {
                                target.cate = this.saveDialog.formData.cate;
                                target.cwd = this.saveDialog.formData.cwd;
                                target.async = this.saveDialog.formData.async;
                                target.script = this.saveDialog.formData.script;
                                target.command = this.saveDialog.formData.command;
                                target.immediate = this.saveDialog.formData.immediate;
                                target.content = this.saveDialog.formData.content;
                                target.description = this.saveDialog.formData.description;

                                const isNewCate = !this.cateList.value.find(f => f.name === target.cate);
                                if (isNewCate) this.cateList.value.push({ name: target.cate });

                                this.curSelector.data = { ...target };

                                // @ts-ignore
                                delete target.from;
                                await sqlite.nota.updateDatas([target]);
                                this.buildNotaData(false);
                            }
                            break;
                        }
                    }

                    this.saveDialog.close();
                } catch (error) {
                    console.error(error);
                }
            });
        });

        // 执行对话框确定回调
        this.runDialog.setConfirmCB(() => {
            if (this.curSelector.data?.script) {
                this.execScript({
                    cwd: this.runDialog.formData.cwd,
                    scriptContent: this.runDialog.formData.content,
                });
            } else {
                this.runCommands({
                    cwd: this.runDialog.formData.cwd,
                    async: this.curSelector.data?.async || false,
                    commands: this.runDialog.formData.content.split(/\\n/),
                });
            }
            this.runDialog.close();
        });

        // 导出数据对话框确定回调
        this.exportDataDialog.setConfirmCB(async () => {
            try {
                const ret = await electronUtils.showOpenDialog({ type: 'selectDir' });
                if (!ret.isCancel && ret.paths) {
                    const saveDirPath = ret.paths[0];
                    const saveFilePath = saveDirPath + '/nota.json';
                    let saveData: Array<INotaData> = JSON.parse(JSON.stringify(this.allNotaData.value));
                    saveData = saveData.filter(t => t.from === 'custom');

                    // 删除无需导出的字段
                    // @ts-ignore
                    const fields = Object.keys(this.exportDataDialog.formData).filter(f => !this.exportDataDialog.formData[f]);
                    for (const item of saveData) {
                        for (const field of fields) {
                            // @ts-ignore
                            delete item[field];
                        }
                    }

                    electronUtils.ipcRenderer.send(IpcMainEventEnum.WRITE_JSON, { filepath: saveFilePath, content: saveData });
                    ElMessage.success('导出数据成功');

                    this.exportDataDialog.close();
                }
            } catch (error) {
                console.error(error);
            }
        });
    }

    /**
     * 选择数据项
     */
    selectItemData(index?: number) {
        if (index !== undefined) {
            const target = this.renderNotaData.value[index];
            if (target) {
                this.curSelector.index = index;
                this.curSelector.data = { ...target };
                this.initTerminal();
                return;
            }
        }

        this.curSelector.index = null;
        this.curSelector.data = null;
    }

    /**
     * 删除 Nota 数据项
     */
    deleteNotaData() {
        this.lockMousetrapEvent.value = true;
        LvMsgBox.confirm('是否确定删除该记录', '警告', {
            type: 'warning',
        })
            .then(async () => {
                const targetIndex = this.allNotaData.value.findIndex(f => f.id === this.curSelector.data?.id);
                if (targetIndex > -1) {
                    await sqlite.nota.removeData(this.allNotaData.value[targetIndex].id);
                    this.allNotaData.value.splice(targetIndex, 1);
                }
                this.filterNotaData();
            })
            .finally(() => {
                this.lockMousetrapEvent.value = false;
            });
    }

    /**
     * 编辑数据
     */
    saveDialogByEdit() {
        this.saveDialog.formData.cate = this.curSelector.data?.cate || '';
        this.saveDialog.formData.cwd = this.curSelector.data?.cwd || '';
        this.saveDialog.formData.content = this.curSelector.data?.content || '';
        this.saveDialog.formData.description = this.curSelector.data?.description || '';
        this.saveDialog.formData.async = this.curSelector.data?.async || false;
        this.saveDialog.formData.script = this.curSelector.data?.script || false;
        this.saveDialog.formData.command = this.curSelector.data?.command || false;
        this.saveDialog.formData.immediate = this.curSelector.data?.immediate || false;
        this.saveDialog.open('edit');
    }

    /**
     * 导入数据
     */
    async importData() {
        try {
            const ret = await electronUtils.showOpenDialog({ type: 'selectFile' });
            const result = await electronUtils.readFile(ret.paths[0]);
            const content: Array<INotaData> = JSON.parse(result.content);
            await sqlite.nota.updateDatas(content);
            await this.buildNotaData();
            ElMessage.success('导入数据成功');
        } catch (error) {
            console.error(error);
        }
    }

    /**
     * 搜索输入框 Focus
     */
    searchInputFocus() {
        this.searchInputRef.value.focus?.();
    }

    /**
     * 根据搜索关键字过滤数据，也可调用来刷新数据
     */
    filterNotaData(initSelect = true) {
        if (this.searchKeyWord.value) {
            this.renderNotaData.value = this.allNotaData.value.filter(item => {
                return item.description.includes(this.searchKeyWord.value);
            });
        } else {
            this.renderNotaData.value = JSON.parse(JSON.stringify(this.allNotaData.value));
        }

        nextTick(() => {
            if (initSelect) {
                this.selectItemData(0);
            }
        });
    }

    /**
     * 触发脚本或命令执行
     */
    triggerCommandOrScript() {
        if (this.curSelector?.data?.script || this.curSelector?.data?.command) {
            if (this.curSelector.data.script) {
                if (this.curSelector.data.immediate) {
                    this.execScript({
                        cwd: this.curSelector.data.cwd,
                        scriptContent: this.curSelector.data.content,
                    });
                    return;
                }
            }

            if (this.curSelector.data.command) {
                if (this.curSelector.data.immediate) {
                    this.runCommands({
                        cwd: this.curSelector.data.cwd,
                        async: this.curSelector.data.async,
                        commands: this.curSelector.data.content.split(/\n|\r/),
                    });
                    return;
                }
            }

            this.runDialog.formData.cwd = this.curSelector.data.cwd || '';
            this.runDialog.formData.content = this.curSelector.data.content;
            this.runDialog.open();
        }
    }

    /**
     * 执行脚本
     */
    async execScript(params: { cwd?: string; scriptContent: string }) {
        const notification = ElNotification({
            duration: 0,
            type: 'info',
            showClose: false,
            message: '正在执行...',
            position: 'bottom-right',
        });
        try {
            this.lockMousetrapEvent.value = true;
            const result = await electronUtils.execScript(params);
            if (result.stderr) {
                this.writeScriptTerminal(result.stderr || '执行失败！', params.cwd);
            } else {
                this.writeScriptTerminal(result.stdout || '执行成功！', params.cwd);
            }
        } catch (error) {
            console.error(error);
        } finally {
            this.lockMousetrapEvent.value = false;
            setTimeout(() => {
                notification.close();
            }, 200);
        }
    }

    /**
     * 执行命令
     */
    async runCommands(params: { cwd?: string; async: boolean; commands: string[] }) {
        const notification = ElNotification({
            duration: 0,
            type: 'info',
            showClose: false,
            message: '正在执行...',
            position: 'bottom-right',
        });
        try {
            this.lockMousetrapEvent.value = true;
            if (params.async) {
                await Promise.all(
                    params.commands.map(async command => {
                        const result = await electronUtils.runCommands({ ...params, commands: [command] });
                        result.forEach(r => {
                            if (r.stderr) {
                                this.writeCommandTerminal(r.command, r.stderr || '执行失败！', params.cwd);
                            } else {
                                this.writeCommandTerminal(r.command, r.stdout || '执行成功！', params.cwd);
                            }
                        });
                    }),
                );
            } else {
                for (const command of params.commands) {
                    const result = await electronUtils.runCommands({ ...params, commands: [command] });
                    result.forEach(r => {
                        if (r.stderr) {
                            this.writeCommandTerminal(r.command, r.stderr || '执行失败！', params.cwd);
                        } else {
                            this.writeCommandTerminal(r.command, r.stdout || '执行成功！', params.cwd);
                        }
                    });
                }
            }
        } catch (error) {
            console.error(error);
        } finally {
            this.lockMousetrapEvent.value = false;
            setTimeout(() => {
                notification.close();
            }, 200);
        }
    }

    /**
     * 初始化终端（单例）
     */
    initTerminal() {
        if (!this.terminalInstance) {
            nextTick(() => {
                const fitAddon = new FitAddon();
                const term = new xterm.Terminal({
                    fontSize: 12,
                    cursorWidth: 2,
                    theme: GithubTheme,
                });
                term.loadAddon(fitAddon);
                this.terminalInstance = term;

                const container = document.querySelector('.terminal-container') as HTMLElement;
                if (container) {
                    term.open(container);
                    term.writeln('$ 欢迎使用 Xterm 终端！');
                    nextTick(() => {
                        fitAddon.fit();
                    });
                }
            });
        }
    }

    /**
     * 脚本结果输出终端
     */
    writeScriptTerminal(result: string, cwd?: string) {
        this.terminalInstance?.writeln('');
        this.terminalInstance?.writeln('');

        const cwdLineText = `$ ${help.terminalTxtColor.blue(cwd || '/path/to/teletool/app/install/folder')}`;
        this.terminalInstance?.writeln(cwdLineText);

        this.terminalInstance?.writeln(`$ ${result}`);
    }

    /**
     * 命令结果输出终端
     */
    writeCommandTerminal(command: string, result: string, cwd?: string) {
        this.terminalInstance?.writeln('');
        this.terminalInstance?.writeln('');

        const cwdLineText = `$ ${help.terminalTxtColor.blue(cwd || '/path/to/teletool/app/install/folder')}`;
        this.terminalInstance?.writeln(cwdLineText);

        const commandLineText = `$ ${help.terminalTxtColor.green(command)}`;
        this.terminalInstance?.writeln(commandLineText);

        this.terminalInstance?.writeln(`$ ${result}`);
    }

    /**
     * 选择 CWD 路径
     */
    async selectCwdPath() {
        try {
            const ret = await electronUtils.showOpenDialog({ type: 'selectDir' });
            if (!ret.isCancel && ret.paths) {
                if (this.saveDialog.visible.value) {
                    this.saveDialog.formData.cwd = ret.paths[0];
                } else if (this.runDialog.visible.value) {
                    this.runDialog.formData.cwd = ret.paths[0];
                }
            }
        } catch (error) {
            console.error(error);
        }
    }
}
